﻿using System;
using System.Data.Services.Providers;
using System.Reflection;
using umbraco.DataServices.Linq;
using System.Linq;

namespace umbraco.DataServices {

    public static class ResourceTypeExtensions {

        public static void AddKeyProperty(this ResourceType resourceType, string name) {
            AddPrimitiveProperty(resourceType, name, true);
        }

        public static void AddPrimitiveProperty(this ResourceType resourceType, string name) {
            AddPrimitiveProperty(resourceType, name, false);
        }

        private static void AddPrimitiveProperty(this ResourceType resourceType, string name, bool isKey) {
            var propertyInfo = resourceType.InstanceType.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
            if (propertyInfo == null)
                throw new ArgumentException("Can't add a property which does not exist on the instance type.");

            var type = ResourceType.GetPrimitiveResourceType(propertyInfo.PropertyType);
            var kind = ResourcePropertyKind.Primitive;
            if (isKey)
                kind |= ResourcePropertyKind.Key;
            var property = new ResourceProperty(propertyInfo.Name, kind, type);
            property.CanReflectOnInstanceTypeProperty = true;
            resourceType.AddProperty(property);
        }

        public static ResourceProperty AddRelationshipSet(this ResourceType resourceType, string name, ResourceType target) {
            var property = new ResourceProperty(name, ResourcePropertyKind.ResourceSetReference, target);
            property.CanReflectOnInstanceTypeProperty = true;
            resourceType.AddProperty(property);
            return property;
        }

        public static ResourceProperty AddRelationshipReference(this ResourceType resourceType, string name, ResourceType target) {
            var property = new ResourceProperty(name, ResourcePropertyKind.ResourceReference, target);
            property.CanReflectOnInstanceTypeProperty = true;
            resourceType.AddProperty(property);
            return property;
        }

        public static string CreateNewNameForDuplicateProperty(this ResourceType resourceType, string alias)
        {
            //Append Property Until
            while (HasDuplicateProperty(resourceType, alias)) {
                alias = alias + "Property";
            }
            return alias;
        }

        public static bool HasDuplicateProperty(this ResourceType resourceType, string alias) {
            return resourceType.Properties.Any(p => p.Name.Equals(alias, StringComparison.InvariantCultureIgnoreCase));
        }

        public static void AddDynamicProperty(this ResourceType resourceType, string name, Type propertyType)  {
            if (string.IsNullOrEmpty(name))
                return;

            //We Need To Deal With Duplicate Property Names... Such As Document Named CreateDate And Property Named CreateDate
            var propertyName = name;
            if (resourceType.HasDuplicateProperty(name))
                propertyName = resourceType.CreateNewNameForDuplicateProperty(name);

            //Create Property
            var type = ResourceType.GetPrimitiveResourceType(propertyType);
            var property = new ResourceProperty(propertyName, ResourcePropertyKind.Primitive, type);
            property.CanReflectOnInstanceTypeProperty = false;
            property.CustomState = new PropertyIdentifier(0, name); //Used To Find Property Type
            resourceType.AddProperty(property);
        }

        public static ResourceSet AddResourceSet(this ResourceType entityType, string name) {
            if (entityType.ResourceTypeKind != ResourceTypeKind.EntityType)
                throw new ArgumentException("The resource type specified as the base type of a resource set is not an entity type.");
            var resourceSet = new ResourceSet(name, entityType);
            resourceSet.SetReadOnly();
            return resourceSet;
        }

    }
}
